Newer
Older
taehui / taehui-fe / src / app / [language] / forum / [forumID] / edit / [[...essayID]] / page.tsx
@Taehui Taehui on 17 Mar 11 KB 2024-03-18 오전 12:51
"use client";

import AutoEssayTitleView from "@/app/[language]/forum/components/AutoEssayTitleView";
import TextView from "@/app/[language]/forum/components/TextView";
import useGetAutoEssay from "@/app/[language]/forum/query/useGetAutoEssay";
import useGetEssay from "@/app/[language]/forum/query/useGetEssay";
import usePostAutoEssay from "@/app/[language]/forum/query/usePostAutoEssay";
import usePostEssay from "@/app/[language]/forum/query/usePostEssay";
import usePostFile from "@/app/[language]/forum/query/usePostFile";
import usePutAutoEssay from "@/app/[language]/forum/query/usePutAutoEssay";
import usePutEssay from "@/app/[language]/forum/query/usePutEssay";
import { useForumStore, useTaehuiStore } from "@/state/Stores";
import { observer } from "mobx-react-lite";
import { useTranslations } from "next-intl";
import { useParams } from "next/navigation";
import { useEffect, useLayoutEffect, useRef, useState } from "react";
import { toast } from "react-toastify";
import {
  Button,
  Col,
  Collapse,
  Input,
  ListGroup,
  Modal,
  ModalBody,
  Row,
  TabContent,
  TabPane,
} from "reactstrap";
import { useTo, useWindowArea } from "taehui-ts/fe-utilities";

export default observer(() => {
  const {
    isTitleTextFilled,
    title,
    text,
    setTitle,
    setText,
    autoEssayID,
    setAutoEssayID,
  } = useForumStore();
  const { titleViewHeight, avatarViewHeight } = useTaehuiStore();
  const { taehuiAvatarID, taehuiAvatarName } = useTaehuiStore();

  const t = useTranslations();
  const [isTestMode, setTestMode] = useState(false);
  const [testText, setTestText] = useState("");
  const [textViewHeight, setTextViewHeight] = useState(100);
  const [isAutoEssayOpened, setAutoEssayOpened] = useState(false);

  const { forumID, essayID } = useParams<{
    forumID: string;
    essayID?: string;
  }>();

  const { isFetched: isAutoEssayLoaded, data: autoEssay } = useGetAutoEssay(
    forumID,
    isAutoEssayOpened,
  );

  const editView = useRef<HTMLDivElement>(null);
  const textView = useRef<HTMLTextAreaElement>(null);
  const inputView = useRef<HTMLDivElement>(null);

  const { mutateAsync: postAutoEssay } = usePostAutoEssay();
  const { mutateAsync: putAutoEssay } = usePutAutoEssay();
  const { mutateAsync: putEssay } = usePutEssay();
  const { isPending, mutateAsync: postFile } = usePostFile();
  const { mutateAsync: postEssay } = usePostEssay();

  const {
    data: essay,
    data: { forumTitle },
    isFetched: isEssayLoaded,
  } = useGetEssay(essayID);

  useEffect(() => {
    if (isEssayLoaded) {
      setTitle(essay.title);
      setText(essay.text);
    }
  }, [essay, isEssayLoaded, setText, setTitle]);

  const { windowHeight } = useWindowArea();

  const to = useTo();

  const setTag = (tag: string) => {
    const { current } = textView;
    if (current) {
      const { selectionStart, selectionEnd } = current;
      const t = text.substring(selectionStart, selectionEnd);
      const tag0 = "<" + tag + ">";
      const tag1 = "</" + tag + ">";
      if (t.startsWith(tag0) || t.endsWith(tag1)) {
        setText(
          text.substring(0, selectionStart) +
            text.substring(
              selectionStart + tag0.length,
              selectionStart + t.length - tag1.length,
            ) +
            text.substring(selectionEnd),
        );
        setTimeout(() => {
          current.selectionStart = selectionStart;
          current.selectionEnd = selectionEnd - tag0.length - tag1.length;
          current.focus();
        }, 0);
      } else {
        setText(
          text.substring(0, selectionStart) +
            tag0 +
            text.substring(selectionStart, selectionEnd) +
            tag1 +
            text.substring(selectionEnd),
        );
        setTimeout(() => {
          current.selectionStart = selectionStart;
          current.selectionEnd = selectionEnd + tag0.length + tag1.length;
          current.focus();
        }, 0);
      }
    }
  };

  useEffect(() => {
    setTextViewHeight(
      windowHeight -
        titleViewHeight -
        avatarViewHeight -
        (editView.current?.clientHeight ?? 0) -
        (inputView.current?.clientHeight ?? 0),
    );
  }, [avatarViewHeight, titleViewHeight, windowHeight]);

  useEffect(() => {
    if (!essayID) {
      setTitle("");
      setText("");
    }
  }, [essayID, setText, setTitle]);

  useEffect(() => {
    const postAutoEssaysID = setInterval(async () => {
      if (isTitleTextFilled) {
        if (typeof autoEssayID === "number") {
          await putAutoEssay({ autoEssayID, title, text });
        } else {
          const { autoEssayID } = await postAutoEssay({
            forumID,
            title,
            text,
          });
          setAutoEssayID(autoEssayID);
        }
      }
    }, 60000);

    return () => {
      clearInterval(postAutoEssaysID);
    };
  }, [
    autoEssayID,
    forumID,
    isTitleTextFilled,
    postAutoEssay,
    putAutoEssay,
    setAutoEssayID,
    t,
    text,
    title,
  ]);

  useLayoutEffect(() => {
    if (isTestMode) {
      setTestText(text);
    }
  }, [isTestMode, text]);

  return (
    <>
      {!isTestMode && (
        <div ref={editView}>
          <Row className="g-0">
            <Col className="m-1">
              <Input
                invalid={!title}
                valid={!!title}
                placeholder={t("title")}
                value={title}
                onChange={({ target: { value } }) => {
                  setTitle(value);
                }}
              />
            </Col>
          </Row>
          <Row className="g-0">
            <Col className="m-1" xs="auto">
              <Button
                onClick={() => {
                  const inputElement = document.createElement("input");
                  inputElement.type = "file";
                  inputElement.accept = "audio/*,image/*,video/*";
                  inputElement.addEventListener(
                    "change",
                    async ({ target }) => {
                      const file = (target as HTMLInputElement).files?.[0];
                      const { current } = textView;
                      if (file && current) {
                        const text = await postFile({
                          file,
                          textView: current,
                        });
                        if (text) {
                          setText(text);
                        }
                      }
                    },
                  );
                  inputElement.click();
                }}
                color="info"
              >
                {t("fileUpload")}
              </Button>
            </Col>
            <Col className="m-1" xs="auto">
              <Button
                onClick={() => {
                  setAutoEssayOpened((prevState) => !prevState);
                }}
                color={isAutoEssayOpened ? "secondary" : "primary"}
              >
                {t("autoEssays")}
              </Button>
            </Col>
          </Row>
          <Row className="g-0">
            <Col className="m-1">
              <Collapse isOpen={isAutoEssayOpened}>
                <ListGroup>
                  {isAutoEssayLoaded ? (
                    autoEssay.map((autoEssay) => (
                      <AutoEssayTitleView
                        key={autoEssay.autoEssayID}
                        autoEssay={autoEssay}
                      />
                    ))
                  ) : (
                    <AutoEssayTitleView
                      autoEssay={{
                        autoEssayID: 0,
                        title: "Loading...",
                        text: "",
                        date: "",
                      }}
                    />
                  )}
                </ListGroup>
              </Collapse>
            </Col>
          </Row>
        </div>
      )}
      <Row className="g-0">
        <Col className="m-1">
          <TabContent activeTab={isTestMode ? 2 : 1}>
            <TabPane tabId={1}>
              <Input
                type="textarea"
                className="form-control"
                placeholder={t("text")}
                value={text}
                onChange={({ target: { value } }) => {
                  setText(value);
                }}
                innerRef={textView}
                style={{ height: textViewHeight }}
              />
            </TabPane>
            <TabPane tabId={2}>
              <TextView
                forumTitle={forumTitle}
                title={title}
                text={testText}
                avatarID={taehuiAvatarID}
                avatarName={taehuiAvatarName}
              />
            </TabPane>
          </TabContent>
        </Col>
      </Row>
      <div ref={inputView}>
        <Row className="g-0">
          <Col className="m-1" xs="auto">
            <Button
              onClick={() => {
                setTag("strong");
              }}
              color="info"
            >
              <strong>{t("textTag")}</strong>
            </Button>
          </Col>
          <Col className="m-1" xs="auto">
            <Button
              onClick={() => {
                setTag("i");
              }}
              color="info"
            >
              <i>{t("textTag")}</i>
            </Button>
          </Col>
          <Col className="m-1" xs="auto">
            <Button
              onClick={() => {
                setTag("u");
              }}
              color="info"
            >
              <u>{t("textTag")}</u>
            </Button>
          </Col>
          <Col className="m-1" xs="auto">
            <Button
              onClick={() => {
                setTag("s");
              }}
              color="info"
            >
              <s>{t("textTag")}</s>
            </Button>
          </Col>
        </Row>
        <Row className="g-0">
          <Col className="m-1">
            <Button
              color={isTestMode ? "primary" : "secondary"}
              onClick={() => {
                setTestMode((prevState) => !prevState);
              }}
            >
              {t("viewEditedEssay")}
            </Button>
          </Col>
          {essayID ? (
            <Col className="m-1" xs="auto">
              <Button
                color="warning"
                onClick={async () => {
                  if (title && text) {
                    await putEssay({ essayID, title, text });
                    to(`/forum/${forumID}/${essayID}`);
                  } else {
                    toast.error(t("failedValidation"));
                  }
                }}
              >
                {t("doModifyEssay")}
              </Button>
            </Col>
          ) : (
            <Col className="m-1" xs="auto">
              <Button
                color="success"
                onClick={async () => {
                  if (title && text) {
                    const { essayID } = await postEssay({
                      forumID,
                      title,
                      text,
                    });
                    to(`/forum/${forumID}/${essayID}`);
                  } else {
                    toast.error(t("failedValidation"));
                  }
                }}
              >
                {t("postEssay")}
              </Button>
            </Col>
          )}
          <Col className="m-1" xs="auto">
            <Button
              color="danger"
              onClick={() => {
                to(`/forum/${forumID}`);
              }}
            >
              {t("quit")}
            </Button>
          </Col>
        </Row>
      </div>
      <Modal isOpen={isPending}>
        <ModalBody>
          <span>{t("fileUploading")}</span>
        </ModalBody>
      </Modal>
    </>
  );
});